iT邦幫忙

2024 iThome 鐵人賽

DAY 29
2
DevOps

在Local建立完整的開發環境筆記系列 第 29

Day 29:使用 Vault 管理 Golang 應用和 GitLab CI/CD 的憑證

  • 分享至 

  • xImage
  •  

使用 Vault 管理 Golang 應用和 GitLab CI/CD 的憑證

1. 簡介

在現代軟體開發中,安全地管理憑證是一個關鍵挑戰。本文將介紹如何使用 HashiCorp Vault 來管理 Golang 應用程序和 GitLab CI/CD 流程中的敏感信息。我們將基於之前的 Golang Web 應用和 GitLab CI 配置,展示如何整合 Vault 以提高安全性。

2. Vault 配置

首先,我們需要在 Vault 中設置適當的密鑰和策略。

2.1 創建密鑰

# 為 Golang 應用創建密鑰
vault kv put secret/myapp/config DB_PASSWORD=mypassword API_KEY=myapikey

# 為 GitLab CI 創建密鑰
vault kv put secret/myapp/ci KUBE_CONFIG=<base64_encoded_kubeconfig>
vault kv put secret/myapp/docker DOCKER_PASSWORD=mydockerhubpassword

2.2 創建策略

# myapp-policy.hcl
path "secret/data/myapp/config" {
  capabilities = ["read"]
}

path "secret/data/myapp/ci" {
  capabilities = ["read"]
}

path "secret/data/myapp/docker" {
  capabilities = ["read"]
}

應用策略:

vault policy write myapp-policy myapp-policy.hcl

2.3 創建 Vault 角色

對於 Kubernetes 環境:

vault write auth/kubernetes/role/myapp \
    bound_service_account_names=myapp-sa \
    bound_service_account_namespaces=default \
    policies=myapp-policy \
    ttl=1h

3. 修改 Golang 應用

我們需要修改 Golang 應用以使用 Vault 客戶端庫。

3.1 安裝 Vault 客戶端

go get github.com/hashicorp/vault/api

3.2 更新 main.go

package main

import (
    "fmt"
    "net/http"
    "os"
    "github.com/hashicorp/vault/api"
)

func getSecret(key string) (string, error) {
    config := &api.Config{
        Address: os.Getenv("VAULT_ADDR"),
    }
    client, err := api.NewClient(config)
    if err != nil {
        return "", err
    }

    token := os.Getenv("VAULT_TOKEN")
    client.SetToken(token)

    secret, err := client.Logical().Read("secret/data/myapp/config")
    if err != nil {
        return "", err
    }

    data, ok := secret.Data["data"].(map[string]interface{})
    if !ok {
        return "", fmt.Errorf("Data not found")
    }

    value, ok := data[key].(string)
    if !ok {
        return "", fmt.Errorf("Key not found")
    }

    return value, nil
}

func handler(w http.ResponseWriter, r *http.Request) {
    apiKey, err := getSecret("API_KEY")
    if err != nil {
        http.Error(w, "Error retrieving secret", http.StatusInternalServerError)
        return
    }
    fmt.Fprintf(w, "Hello, World from Golang! API Key: %s", apiKey)
}

func main() {
    http.HandleFunc("/", handler)
    fmt.Println("Server starting on port 8080...")
    http.ListenAndServe(":8080", nil)
}

4. 更新 Dockerfile

我們需要確保 Vault 客戶端在容器中可用:

FROM golang:1.16-alpine AS builder
WORKDIR /app
COPY . .
RUN apk add --no-cache git && \
    go get github.com/hashicorp/vault/api && \
    go build -o main .

FROM alpine:latest
WORKDIR /root/
COPY --from=builder /app/main .
EXPOSE 8080
CMD ["./main"]

5. 更新 Kubernetes Deployment

修改 deployment.yaml 以包含 Vault 代理注入:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: golang-web-app
  annotations:
    vault.hashicorp.com/agent-inject: "true"
    vault.hashicorp.com/agent-inject-secret-config: "secret/data/myapp/config"
    vault.hashicorp.com/role: "myapp"
spec:
  replicas: 1
  selector:
    matchLabels:
      app: golang-web-app
  template:
    metadata:
      labels:
        app: golang-web-app
    spec:
      serviceAccountName: myapp-sa
      containers:
      - name: golang-web-app
        image: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHA}
        ports:
        - containerPort: 8080
        env:
        - name: VAULT_ADDR
          value: "http://vault:8200"

6. 更新 GitLab CI 配置

修改 .gitlab-ci.yml 以使用 Vault:

stages:
  - build
  - deploy

variables:
  VAULT_ADDR: "http://vault:8200"

build:
  stage: build
  image: docker:20.10.16
  services:
    - docker:20.10.16-dind
  script:
    - apk add --no-cache curl jq
    - |
      VAULT_TOKEN=$(curl -s -X POST ${VAULT_ADDR}/v1/auth/jwt/login -d "{\"role\":\"myapp\",\"jwt\":\"$CI_JOB_JWT\"}" | jq -r '.auth.client_token')
    - |
      DOCKER_PASSWORD=$(curl -s -H "X-Vault-Token: $VAULT_TOKEN" ${VAULT_ADDR}/v1/secret/data/myapp/docker | jq -r '.data.data.DOCKER_PASSWORD')
    - echo $DOCKER_PASSWORD | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

deploy:
  stage: deploy
  image: 
    name: bitnami/kubectl:latest
    entrypoint: ['']
  script:
    - apk add --no-cache curl jq
    - |
      VAULT_TOKEN=$(curl -s -X POST ${VAULT_ADDR}/v1/auth/jwt/login -d "{\"role\":\"myapp\",\"jwt\":\"$CI_JOB_JWT\"}" | jq -r '.auth.client_token')
    - |
      KUBE_CONFIG=$(curl -s -H "X-Vault-Token: $VAULT_TOKEN" ${VAULT_ADDR}/v1/secret/data/myapp/ci | jq -r '.data.data.KUBE_CONFIG')
    - echo $KUBE_CONFIG | base64 -d > kubeconfig
    - export KUBECONFIG=kubeconfig
    - sed -i "s|${CI_REGISTRY_IMAGE}|${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHA}|g" deployment.yaml
    - kubectl apply -f deployment.yaml

7. 安全考慮

  1. 最小權限原則:確保 Vault 策略只提供必要的訪問權限。
  2. 短期令牌:使用短期令牌和自動續期來減少風險。
  3. 安全的令牌傳遞:避免在環境變量或配置文件中存儲 Vault 令牌。
  4. 審計日誌:啟用 Vault 的審計日誌功能,監控所有訪問。

8. 最佳實踐

  1. 密鑰輪換:定期輪換所有密鑰和憑證。
  2. 環境隔離:為不同環境(開發、測試、生產)使用不同的 Vault 路徑和策略。
  3. 集成測試:包含 Vault 集成在您的測試套件中。
  4. 監控和告警:設置監控以檢測異常的 Vault 訪問模式。

9. 結論

通過將 Vault 整合到 Golang 應用和 GitLab CI/CD 流程中,我們大大提高了憑證管理的安全性和靈活性。這種方法允許集中管理敏感信息,減少了憑證洩露的風險,並簡化了密鑰輪換過程。

然而,正確實施這種集成需要仔細的規劃和持續的維護。確保定期審查和更新您的 Vault 策略和實踐,以應對不斷變化的安全威脅。

通過採用這些最佳實踐,您可以創建一個更安全、更可管理的基礎設施,為您的應用程序和部署流程提供強大的保護。


上一篇
Day 28:Vault
下一篇
Day 30:結語
系列文
在Local建立完整的開發環境筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言